package com.za.plugin.transfer.form.insertupdate;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;
import com.za.plugin.util.SqlUtil;
import org.apache.ibatis.mapping.ParameterMapping;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * replace into 转为 merge into
 */
public class ReplaceIntoStyleTransfer implements StyleTransfer {
    @Override
    public boolean isSupport(String sql) {
        return sql.trim().startsWith("replace into") || sql.trim().startsWith("REPLACE INTO");
    }

    @Override
    public String transfer(String sql, String tableName, List<String> autoIncrProperties,
                           List<ParameterMapping> parameterMappingsCopy, List<ParameterMapping> parameterMappings,
                           Set<String> pKs, Map<String, List<String>> pkAndUniqueKeys) {
        MySqlStatementParser parser = new MySqlStatementParser(sql);
        SQLStatement sqlStatement = parser.parseStatement();
        List<String> keys = SqlUtil.getKeysFromParameterMappingsCopy(parameterMappingsCopy);

        // 检测到批量插入100条数据就分割sql
        // int batch = 100;
        // if (sqlStatement instanceof MySqlReplaceStatement) {
        //     List<SQLInsertStatement.ValuesClause> valuesList = ((MySqlReplaceStatement) sqlStatement).getValuesList();
        //     int size = valuesList.size();
        //     int count = size / batch;
        //     int other = size % batch;
        //
        //     List<SQLExpr> values = ((MySqlReplaceStatement) sqlStatement).getValuesList().get(0).getValues();
        //     List<SQLExpr> columns = ((MySqlReplaceStatement) sqlStatement).getColumns();
        //     List<String> columnList = SqlUtil.ExprToString(columns);
        //     List<String> valueList = SqlUtil.ExprToString(values);
        //     HashMap<String, String> map = new LinkedHashMap<>();
        //     for (int i = 0; i < columnList.size(); i++) {
        //         map.put(columnList.get(i), valueList.get(i));
        //     }
        //     String tableNameByParse = ((MySqlReplaceStatement) sqlStatement).getTableName().getSimpleName();
        //
        //     StringBuilder sb = new StringBuilder();
        //     if (count > 0) {
        //         for (int i = 0; i < count; i++) {
        //             String subSql = "merge into " + tableNameByParse + " o using( " + propFromDual(map, autoIncrProperties, batch, parameterMappings, parameterMappingsCopy, i)
        //                     + " ) t on " + onCond(pKs, map) + " when matched then update set " +
        //                     matchCond(pKs, autoIncrProperties, map) +
        //                     " when not matched then insert " + unMatchCond(autoIncrProperties, map) + ";";
        //             sb.append(subSql);
        //         }
        //     }
        //     if (other > 0) {
        //         String subSql = "merge into " + tableNameByParse + " o using( " + propFromDual(map, autoIncrProperties, other, parameterMappings, parameterMappingsCopy, count)
        //                 + " ) t on " + onCond(pKs, map) + " when matched then update set " +
        //                 matchCond(pKs, autoIncrProperties, map) +
        //                 " when not matched then insert " + unMatchCond(autoIncrProperties, map) + ";";
        //         sb.append(subSql);
        //     }
        //     sql = sb.toString();
        //     return sql;
        // }

        // 可能不会执行到了
        Map<String, ParameterMapping> map = SqlUtil.getPropertyMapFromParameterMappings(parameterMappingsCopy);
        boolean isForEach = SqlUtil.isForEach(parameterMappingsCopy);

        if (isForEach && sql.contains("replace into")) {
            sql = "MERGE INTO " + SqlUtil.getTableName(tableName) + " o using (" + SqlUtil.strFromDualForEach(keys, map,
                    parameterMappings, sql, autoIncrProperties, parameterMappingsCopy) +
                    " ) t on " + SqlUtil.onStr(pKs, keys, true, autoIncrProperties, pkAndUniqueKeys) + " when matched then update set " +
                    SqlUtil.setStrForEach(keys, pKs, autoIncrProperties) + " when not matched then insert " +
                    SqlUtil.insertStrForEach(keys, pKs, sql, autoIncrProperties);

        }
        if (!isForEach && sql.contains("replace into")) { // 单条 replace into
            sql = "MERGE INTO " + SqlUtil.getTableName(tableName) + " o using (" +
                    SqlUtil.strFromDualSimple(keys, map, parameterMappings, sql, pKs, autoIncrProperties)
                    + " ) t on " + SqlUtil.onStr(pKs, keys, false, autoIncrProperties,   pkAndUniqueKeys) + " when matched then update set "
                    + SqlUtil.setStrSimple(keys, map, parameterMappings, pKs, sql, autoIncrProperties) + " when not matched then insert "
                    + SqlUtil.insertStrSimple(keys, map, parameterMappings, pKs, sql, autoIncrProperties);

        }
        return sql;
    }

    private static String unMatchCond(List<String> autoIncrProperties, HashMap<String, String> map) {
        StringBuilder sb = new StringBuilder("(");
        for (Map.Entry<String, String> entry : map.entrySet()) {
            if (!autoIncrProperties.contains(entry.getKey())) {
                sb.append(entry.getKey()).append(",");
            }
        }
        sb.deleteCharAt(sb.length() - 1).append(") values (");
        for (Map.Entry<String, String> entry : map.entrySet()) {
            if (!entry.getValue().contains("?")) {
                sb.append(entry.getValue()).append(",");
                continue;
            }
            if (!autoIncrProperties.contains(entry.getKey())) {
                sb.append("t.").append(entry.getKey()).append(",");
            }
        }
        return sb.deleteCharAt(sb.length() - 1).append(")").toString();
    }

    private static String matchCond(Set<String> pks, List<String> autoIncrProperties, HashMap<String, String> map) {
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String> entry : map.entrySet()) {
            String key = entry.getKey();
            if (!autoIncrProperties.contains(key) && !pks.contains(key) && map.get(key).contains("?")) {
                sb.append("o.").append(key).append("=t.").append(key).append(",");
            }
        }
        return sb.deleteCharAt(sb.length() - 1).toString();
    }

    private static String onCond(Set<String> pks, Map<String, String> map) {
        StringBuilder sb = new StringBuilder();
        if (CollUtil.isEmpty(pks) || !isContain(map, pks)) {
            return "false";
        }
        for (String pk : pks) {
            sb.append("o.").append(pk).append("=t.").append(pk).append(" and ");
        }
        return sb.delete(sb.lastIndexOf(" and "), sb.length()).toString();
    }

    private static boolean isContain(Map<String, String> map, Set<String> pks) {
        for (String pk : pks) {
            if (!map.containsKey(pk)) {
                return false;
            }
        }
        return true;
    }

    private static String propFromDual(HashMap<String, String> map, List<String> autoIncrProperties, int batch
            , List<ParameterMapping> parameterMappings, List<ParameterMapping> parameterMappingsCopy, int cnt) {
        StringBuilder sb = new StringBuilder();
        sb.append("select ");
        int count = (int) map.values().stream().filter(it -> it.contains("?")).count();
        int j = 0;
        for (int i = 0; i < batch; i++) {

            for (Map.Entry<String, String> entry : map.entrySet()) {
                if (!entry.getValue().contains("?")) {
                    continue;
                }
//                if (autoIncrProperties.contains(entry.getKey())) {
//                    continue;
//                }
                parameterMappings.add(parameterMappingsCopy.get(count * cnt * batch + j));
                j++;
                sb.append("? ").append(entry.getKey()).append(",");
            }
            if (i < batch - 1) {
                sb.deleteCharAt(sb.length() - 1).append(" from dual UNION ALL select ");
            }
        }
        return sb.deleteCharAt(sb.length() - 1).append(" from dual ").toString();
    }

}
